#include <sys/timeb.h>
#include <fstream>
#include <limits>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include "util.h"

bool GDebug = false;
Config *G_CONFIG = NULL;
const uint64 G_PAGE_SIZE = sysconf(_SC_PAGESIZE);

void InitUtil(int argc, char **argv) {
  if (G_CONFIG) return;
  G_CONFIG = new Config;
  G_CONFIG->Load("config");
  G_CONFIG->LoadCommandLineParams(argc, argv);
  CheckObjectSizes();
  CheckLittleEndian();
  umask(0);
}

void CheckLittleEndian() {
  uint64 a = 0x0102030405060708ull;
  CHECK(*((uint8 *)(&a)) == 8);
}

void CheckObjectSizes() {
  CHECK(sizeof(void *) == 8);
  CHECK(sizeof(int32) == 4);
  CHECK(sizeof(int64) == 8);
  CHECK(sizeof(float) == 4);
  CHECK(sizeof(double) == 8);
}

// Has to be a real file
void Config::Load(string fn) {
  ifstream in(fn.c_str());
  string k, v;
  while (in >> k) {
    if (k != "#") {
      in >> v;
      map_[k] = v;
    }
    char c;
    while (in.get(c) && (c != '\n'));
  }  
}

string Config::GetString(string key) {
  string * look = map_ % key;
  if (!look) {
    cout << "Missing config key " << key << endl;
    CHECK(false);
  }
  return *look;
}

bool Config::Exists(string key) {
  return map_ % key;
}

void Config::LoadCommandLineParams(int argc, char ** argv) {
  for (int64 i=0; i<argc; i++) {
    string s = argv[i];
    if (s.substr(0, 2) != "--") continue;
    s = s.substr(2);
    uint64 pos = s.find('=');
    if (pos == string::npos) continue;
    map_[s.substr(0, pos)]= s.substr(pos+1);
  }
}

// Global lock
pthread_mutex_t G_LOCK = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t G_PRINT_LOCK = PTHREAD_MUTEX_INITIALIZER;

// Constants we need
double TwoToTheMinus64 = pow(2.0, -64);

uint64 RNG::RandomImportance(uint64 external) {
  if (external == 0) return 0;
  int log_external = (int) log(external) / log(2);
  uint64 r = Random();
  uint64 random_bit = ( (r ^ (r-1)) + 1 ) >> 1;
  return (( ((1 << log_external) - 1) | (random_bit << log_external) ) 
	  & 0x7fffffffffffffffull ) || 1;
}

uint64 BitCount(uint64 x) {
  x = (x & 0x5555555555555555ll) + ( (x & 0xAAAAAAAAAAAAAAAAll) >> 1);
  x = (x & 0x3333333333333333ll) + ( (x & 0xCCCCCCCCCCCCCCCCll) >> 2);
  x = (x & 0x0F0F0F0F0F0F0F0Fll) + ( (x & 0xf0f0f0f0f0f0f0f0ll) >> 4);
  x = (x & 0x00ff00ff00ff00ffll) + ( (x & 0xff00ff00ff00ff00ll) >> 8);
  x = (x & 0x0000ffff0000ffffll) + ( (x & 0xffff0000ffff0000ll) >> 16);
  x = (x & 0x00000000ffffffffll) + ( (x & 0xffffffff00000000ll) >> 32);
  return x;
}

int TestRandomImportance() {
  RNG rng;
  int x;
  int y;
  while (cin >> x >> y) {
    uint64 result = 0;
    for (int i=0; i<y; i++) {
      result |= rng.RandomImportance(x);
    }
    cout << (1 << BitCount(result)) << endl;
  }
  return 0;
}

double RNG::RandomFraction() {
  return (Random()+ 0.5) * TwoToTheMinus64;
}

// vector of pairs of (i, v[i]) sorted by decreasing v[i]
// To make this faster, could use a allocated vector and delete it on the other
// side. Or pass in a vector to use.
vector<pair<int, float> > 
DecreasingSortWithIndices(const vector<float>& v) {
  vector<pair<int, float> > ret;
  for (uint32 i=0; i<v.size(); i++) ret.push_back(make_pair(i, v[i]));
  sort(ret.begin(), ret.end(), GreaterSecond<int, float>());
  return ret;
}

string Upcase(const string& s) {
  string ret = s;
  for (uint32 i=0; i<ret.size(); i++)
    ret[i] = toupper(ret[i]);
  return ret;
}

int UltimateDestination(const map<int, int> & m, int source) {
  const int * look = m % source;
  if (look) return UltimateDestination(m, *look);
  return source;
}

void putback(istream & in, string s) {
  for (uint32 cc=0; cc < s.length(); cc++) {
    in.putback(s[s.length() - 1 - cc]);
    // cout << "putback " << s[s.length() - 1 - cc] << endl;
  }
}

string ReadLine (istream & in) {
  string ret;
  char c;
  while (in.get(c) && (c != '\n'))
    ret += c;
  // cout << "Reading about to return " << ret << "-----" << endl;
  return ret;
}

void ReadIntoString(string * res, istream& in) {
  char c;
  while (in.get(c))
    *res += c;
}

void ReadFileIntoString(string * res, string filename) {
  ifstream in(filename.c_str());
  ReadIntoString(res, in);
 }

bool FileExists(string fn) {
  struct stat st;
  return (!stat(fn.c_str(), &st)); }

void DeleteFile(string fn) {
  remove(fn.c_str()); }


 // Terminate the file with a \0
 bool Mmap(string filename, char ** block, uint64 * length) {
   int fd = open (filename.c_str(), O_RDONLY);
   if (fd == -1) return false;
   struct stat st;
   stat(filename.c_str(), &st);
   uint64 filesize = st.st_size;
   char * ret = (char *) mmap(0, filesize+1, PROT_READ, MAP_SHARED, fd, 0);
   *length = filesize;
   *block = ret;
   return true;
 }

 void WriteSizePrefixed(ostream *output, const char *data, uint64 size) {
   size = RoundUpToMultipleOf(size, 8);
   Write(output, size);
   output->write(data, size);
 }


 string itoa(int64 i) {
   ostringstream ostr;
   ostr << i;
   return ostr.str();
 }

 // compute k^n
 uint64 UInt64Pow(uint64 k, uint64 n) {
   uint64 ret = 1;
   while (n) {
     if (n & 1) ret *= k;
     n >>= 1;
     k *= k;
   }
   return ret;
 }

 Memblock ExpandToEncodedNumber(Memblock m) {
   while (!IsStartEncodedNumber(m.data_)) {
     m.data_--; m.size_++; }
   while (!IsStartEncodedNumber(m.data_ + m.size_))
     m.size_++;
   return m;
 }

void Memblock::WriteToFile(string fn) {
  ofstream output(fn.c_str(), ofstream::binary);
  output.write(data_, size_);
}

void Memblock::ReadFromFile(string fn) {
  ifstream input(fn.c_str(), ifstream::binary);
  input.read(data_, size_);
}

void Normalize(vector<double> & v) {
  double sum = 0;
  for (uint64 i=0; i<v.size(); i++) sum += v[i];
  for (uint64 i=0; i<v.size(); i++) v[i] /= sum;;
}
  

